﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Crossing modes
    /// </summary>
    public enum CROSSING_MODE
    {
        HUE_CROSS_VERT_HORZ, //define a cross by crossing vertical and horizontal line through a point
        HUE_CROSS_DIAGONAL_FROM_EDGES, //define a cross by crossing from edges through a point  
        HUE_CROSS_45_DEGREE, //define a cross by crossing through a 45 degree line
        COMPLETE_MATRIX,
        KERNEL_WINDOW
    }

    /// <summary>
    /// This will show certain objects with a concrete structure to you.
    /// </summary>
    public class Crossing : BaseImageFilter
    {
        protected float[] m_aValue1 = null;
        protected float[] m_aValue2 = null;

        protected CROSSING_MODE m_CrossingMode = CROSSING_MODE.HUE_CROSS_VERT_HORZ; //choose simplest mode
        protected ColorCalculation calculation = null;
        protected int m_nWindowSize = 3;

        protected int y = 0;
        protected int x = 0;
        protected int z = 0;
        protected int nHeight = -1;
        protected int nWidth = -1;
        protected float fValue = 0.0f;
        protected float fValue1 = 0.0f;
        protected float fValue2 = 0.0f;
        protected Color color = Color.Transparent;
        protected Color color1 = Color.Transparent;
        protected Color color2 = Color.Transparent;
        protected bool m_bUseAverageValue = true;

        protected bool m_bConvertToGray = true;

        /// <summary>
        /// Default constructor
        /// </summary>
        public Crossing()
        {
            this.M_PROCESSING_MODE = PROCESSING_MODE.GrayValue;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Crossing"/> class.
        /// </summary>
        /// <param name="_mode">The _mode.</param>
        /// <param name="_bUseAverageValue">if set to <c>true</c> [_b use average value].</param>
        public Crossing(CROSSING_MODE _mode, bool _bUseAverageValue)
        {
            this.ProcessingMode = PROCESSING_MODE.GrayValue;
            this.m_CrossingMode = CROSSING_MODE.HUE_CROSS_VERT_HORZ;
        }

        ///// <summary>
        ///// 
        ///// </summary>
        //public Crossing()
        //{
        //}

        /// <summary>
        /// used by clone to create a deep copy
        /// </summary>
        /// <param name="_crossing"></param>
        internal Crossing(Crossing _crossing)
        {
            this.m_CrossingMode = _crossing.m_CrossingMode;
            this.m_bUseAverageValue = _crossing.m_bUseAverageValue;
        }

        /// <summary>
        /// Gets or sets a value indicating whether [use average value].
        /// </summary>
        /// <value><c>true</c> if [use average value]; otherwise, <c>false</c>.</value>
        public bool UseAverageValue
        {
            get { return m_bUseAverageValue; }
            set { m_bUseAverageValue = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [convert to gray].
        /// </summary>
        /// <value><c>true</c> if [convert to gray]; otherwise, <c>false</c>.</value>
        public bool ConvertToGray
        {
            get { return m_bConvertToGray; }
            set { m_bConvertToGray = value; }
        }

        /// <summary>
        /// Gets or sets the crossing mode
        /// </summary>
        public CROSSING_MODE CrossingMode
        {
            get { return this.m_CrossingMode; }
            set { this.m_CrossingMode = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [window size].
        /// </summary>
        /// <value><c>true</c> if [window size]; otherwise, <c>false</c>.</value>
        public int WindowSize
        {
            get { return m_nWindowSize; }
            set { m_nWindowSize = value; }
        }

        /// <summary>
        /// executes this filter
        /// </summary>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            //if vertical/horizontal empty precalculate values.
            calculation = Definitions.ColorCalculationDictionaryProcessingMode[M_PROCESSING_MODE];

            nHeight = _bitmap.Height;
            nWidth = _bitmap.Width;

            switch (m_CrossingMode)
            {
                case CROSSING_MODE.KERNEL_WINDOW:
                    KernelWindow(_bitmap);
                    return _bitmap;

                case CROSSING_MODE.HUE_CROSS_VERT_HORZ:
                    HueCrossVertHorz(_bitmap);
                    break;

                case CROSSING_MODE.HUE_CROSS_45_DEGREE:
                    int nX1;
                    int nX2;
                    HueCross45Degree(_bitmap, out nX1, out nX2);
                    break;

                case CROSSING_MODE.COMPLETE_MATRIX:
                    CompleteMatrix(_bitmap);
                    break;

                case CROSSING_MODE.HUE_CROSS_DIAGONAL_FROM_EDGES:
                    HueCrossDiagonalFromEdges(_bitmap, out nX1, out nX2);
                    break;
            }

            //Display
            if (m_CrossingMode == CROSSING_MODE.COMPLETE_MATRIX)
            {
                ExecuteFullMatrixMode(_bitmap, m_aValue1[0]);
            }
            else
            {
                for (y = 0; y < nHeight; y++)
                {
                    for (x = 0; x < nWidth; x++)
                    {
                        if (m_aValue2.Length>y && m_aValue1.Length>x)
                        {
                            fValue = (m_aValue2[y] + m_aValue1[x]) / 2;
                            int nValue = (int)fValue;
                            _bitmap.SetPixel(x, y, m_bConvertToGray ? Color.FromArgb(nValue, nValue, nValue) : Color.FromArgb(nValue));
                        }
                    }
                }
            }

            return _bitmap;
        }

        /// <summary>
        /// Hues the cross diagonal from edges.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="nX1">The n x1.</param>
        /// <param name="nX2">The n x2.</param>
        private void HueCrossDiagonalFromEdges(UnsafeBitmap _bitmap, out int nX1, out int nX2)
        {
            //precalculate for each pairs of coordinates, the 2 lines,
            //that will cross
            //each coordinate will be assigned a pair of two STRAIGHT LINES (geraden)
            //the slopes for each pair has to be precalculated.#
            //bresenham line should be the solution here. So I get each point
            //from deriving the line
            m_aValue1 = new float[nWidth];
            m_aValue2 = new float[nWidth];
            Point ptLeftUpper = new Point(0, 0);
            Point ptRightUpper = new Point(nWidth, 0);
            Point ptLeftBottom = new Point(0, nHeight);
            Point ptRightBottom = new Point(nWidth, nHeight);

            fValue1 = 0.0f;
            fValue2 = 0.0f;
            nX1 = 0;
            nX2 = 0;
            int nCount1 = 0;
            int nCount2 = 0;

            for (y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    nX1 = x;
                    nX2 = nWidth - x - 1;
                    Point pt = new Point(x, y);
                    //render first line
                    nCount1 = 0;
                    nCount2 = 0;
                    fValue1 = 0.0f;
                    fValue2 = 0.0f;
                    foreach (Point pt1 in Bresenham.RenderLine(ptLeftUpper, pt))
                    {
                        color1 = _bitmap.GetPixel(nX1, y);
                        fValue1 += calculation(color1);
                        nCount1++;
                    }
                    foreach (Point pt2 in Bresenham.RenderLine(ptRightBottom, pt))
                    {
                        color1 = _bitmap.GetPixel(nX2, y);
                        fValue1 += calculation(color1);
                        nCount1++;
                    }

                    foreach (Point pt3 in Bresenham.RenderLine(ptRightUpper, pt))
                    {
                        color2 = _bitmap.GetPixel(nX1, y);
                        fValue2 += calculation(color2);
                        nCount2++;
                    }

                    foreach (Point pt3 in Bresenham.RenderLine(ptLeftBottom, pt))
                    {
                        color2 = _bitmap.GetPixel(nX1, y);
                        fValue2 += calculation(color2);
                        nCount2++;
                    }

                    float newVariable = m_bUseAverageValue ? ((fValue1 / nCount1) + (fValue2 / nCount2) / 2) : fValue;
                    if (newVariable > 255) newVariable = 255;
                    _bitmap.SetGrayPixel(x, y, (int)newVariable);
                }
            }
        }

        /// <summary>
        /// Completes the matrix.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        private void CompleteMatrix(UnsafeBitmap _bitmap)
        {
            //calculate ONE complete value here
            fValue = 0.0f;

            m_aValue1 = new float[1];
            m_aValue2 = new float[1];
            for (y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    color = _bitmap.GetPixel(x, y);
                    fValue += calculation(color);
                }
            }
            m_aValue1[0] = fValue;
            m_aValue2[0] = fValue;
        }

        /// <summary>
        /// Hues the cross45 degree.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="nX1">The n x1.</param>
        /// <param name="nX2">The n x2.</param>
        private void HueCross45Degree(UnsafeBitmap _bitmap, out int nX1, out int nX2)
        {
            //precalculate diagonal values.
            nX1 = 0;
            nX2 = 0;
            m_aValue1 = new float[nWidth];
            m_aValue2 = new float[nWidth];

            for (x = 0; x < nWidth; x++)
            {
                nX1 = x;
                nX2 = nWidth - x - 1;
                for (y = 0; y < nHeight; y++)
                {
                    //Get diagonal from upper left to bottom right
                    if (nX1 > nWidth || nX2 < 0)
                    {
                        continue;
                    }
                    color1 = _bitmap.GetPixel(nX1, y);
                    fValue1 += calculation(color1);

                    //Get diagonal from upper right to bottom left
                    color2 = _bitmap.GetPixel(nX2, y);
                    fValue2 += calculation(color2);
                }
                m_aValue1[x] = fValue;
                m_aValue2[y] = fValue;
            }
        }

        /// <summary>
        /// Hues the cross vert horz.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        private void HueCrossVertHorz(UnsafeBitmap _bitmap)
        {
            //precalculate horizontal and vertical values
            m_aValue2 = new float[nHeight];
            m_aValue1 = new float[nWidth];
            for (y = 0; y < nHeight; y++)
            {
                fValue = 0;
                for (x = 0; x < nWidth; x++)
                {
                    color = _bitmap.GetPixel(x, y);
                    fValue += calculation(color);
                }
                m_aValue2[y] = m_bUseAverageValue ? fValue / nWidth : fValue;
            }
            for (x = 0; x < nWidth; x++)
            {
                fValue = 0;
                for (y = 0; y < nHeight; y++)
                {
                    color = _bitmap.GetPixel(x, y);
                    fValue += calculation(color);
                }
                m_aValue1[x] = m_bUseAverageValue ? fValue / nHeight : fValue;
            }
        }

        /// <summary>
        /// Kernels the window.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        private void KernelWindow(UnsafeBitmap _bitmap)
        {
            int i = 0;
            int j = 0;
            UnsafeBitmap bitmapCloned = (UnsafeBitmap)_bitmap.Clone();
            int nKernelMiddle = m_nWindowSize / 2;

            fValue1 = 0;
            fValue2 = 0;

            for (y = m_nWindowSize; y < nHeight - m_nWindowSize; y++)
            {
                for (x = m_nWindowSize; x < nWidth - m_nWindowSize; x++)
                {
                    int nXTemp = x - nKernelMiddle;
                    int nYTemp = y - nKernelMiddle;
                    for (i = x - nKernelMiddle; i < x + nKernelMiddle; i++)
                    {
                        fValue1 += calculation(bitmapCloned.GetPixel(i, y));
                    }
                    for (j = y - nKernelMiddle; j < y + nKernelMiddle; j++)
                    {
                        fValue2 += calculation(bitmapCloned.GetPixel(x, j));
                    }
                    _bitmap.SetPixel(x, y, Color.FromArgb((int)(fValue1 + fValue2) / 2));
                }
            }
        }

        /// <summary>
        /// Executes the full matrix mode
        /// </summary>
        protected virtual void ExecuteFullMatrixMode(UnsafeBitmap _bitmap, float fValue)
        {
            int nWidth = _bitmap.Width;
            int nHeight = _bitmap.Height;
            int y = 0;
            int x = 0;
            Color color = Color.Transparent;

            int nMaxValue = ColorFunctions.ToIntRGB(Definitions.White);
            for (y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    color = _bitmap.GetPixel(x, y);
                    int nValue = (int)(fValue - ColorFunctions.ToIntRGB(color));
                    if (m_bConvertToGray)
                    {
                        //normalize here
                        nValue = (int)((float)nValue / nMaxValue * 255);
                        if (nValue > 255) nValue = 255;
                        if (nValue < 0) nValue = 0;
                    }
                    _bitmap.SetPixel(x, y, m_bConvertToGray ? Color.FromArgb(nValue,nValue,nValue) : Color.FromArgb(nValue));
                }
            }
        }

        /// <summary>
        /// Create a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Crossing(this);
        }
    }
}
